Книги з мов програмування пояснюють, що типи значень створюються в стеку, а посилальні типи створюються в купі, не пояснюючи, що це за дві речі. Я не прочитав чіткого пояснення цього. Я розумію, що таке стек. Але, Де і що вони перебувають (фізично в пам'яті справжнього комп'ютера)? Наскільки вони контролюються операційною системою чи мовою? Який їх обсяг? Що визначає розмір кожного з них? Що робить його швидшим?
2020-12-07 21:44:17
Стек - це пам’ять, виділена як подряпинний простір для потоку виконання. Коли викликається функція, у верхній частині стека зарезервований блок для локальних змінних та деяких даних бухгалтерії. Коли ця функція повертається, блок стає невикористаним і може бути використаний під час наступного виклику функції. Стек завжди зарезервований у порядку LIFO (останній у першому вихід); останній зарезервований блок завжди є наступним звільненим блоком. Це дозволяє дуже просто відстежувати стек; звільнення блоку зі стеку - це не що інше, як регулювання одного покажчика. Купа - це пам’ять, виділена для динамічного розподілу. На відміну від стека, немає примусового шаблону розподілу та вивільнення блоків з купи; Ви можете виділити блок у будь-який час і звільнити його в будь-який час. Це робить набагато складнішим відстеження того, які частини купи виділені чи вільні в будь-який момент часу; Є багато спеціальних розподілювачів купи, які можна налаштувати ефективність купи для різних моделей використання. Кожен потік отримує стек, тоді як для програми зазвичай є лише одна купа (хоча нерідкі випадки, коли є кілька куп для різних типів розподілу). Щоб відповісти безпосередньо на ваші запитання: Наскільки вони контролюються ОС або мовою виконання? ОС створює стек для кожного потоку рівня системи під час створення потоку. Зазвичай ОС викликається мовою виконання, щоб виділити купу для програми. Який їх обсяг? Стек прикріплюється до нитки, тому, коли нитка виходить із стопки, вона відновлюється. Купа, як правило, виділяється під час запуску програми під час виконання і відновлюється, коли додаток (технічно оброблений) виходить. Що визначає розмір кожного з них? Розмір стека встановлюється, коли створюється потік. Розмір купи встановлюється під час запуску програми, але може зростати по мірі необхідності (розподільник вимагає більше оперативної пам'яті в операційній системі). Що робить його швидшим? Стек швидший, оскільки шаблон доступу робить тривіальним розподіл і вивільнення пам’яті з нього (покажчик / ціле число просто збільшується або зменшується), тоді як купа має набагато складнішу бухгалтерію, яка бере участь у розподілі чи звільненні. Крім того, кожен байт у стеці має тенденцію до повторного використання дуже часто, що означає, що він, як правило, відображається в кеші процесора, що робить його дуже швидким. Ще одним впливом продуктивності для купи є те, що купа, будучи в основному глобальним ресурсом, зазвичай має бути багатопотоковою, тобто кожному розподілу та вивільненню потрібно, як правило, синхронізуватись із "усіма" іншими зверненнями до купи в програмі. Чітка демонстрація: Джерело зображення: vikashazrati.wordpress.com | Стек: Зберігається в оперативній пам’яті комп’ютера, як і купа. Змінні, створені в стеку, вийдуть за межі обсягу та автоматично звільняться. Набагато швидше виділити в порівнянні зі змінними в купі. Реалізовано з фактичною структурою даних стека. Зберігає локальні дані, адреси повернення, що використовуються для передачі параметрів. Може мати переповнення стека, коли використовується занадто велика частина стека (переважно з нескінченної або занадто глибокої рекурсії, дуже великих виділень). Дані, створені в стеку, можна використовувати без покажчиків. Ви використовували б стек, якщо точно знаєте, скільки даних потрібно виділити перед часом компіляції, і він не надто великий. Зазвичай має максимальний розмір, який уже визначений на момент запуску вашої програми. Купи: Зберігається в пам’яті комп’ютера, як і стек. У C ++ змінні в купі повинні бути знищені вручну і ніколи не виходити за межі області дії. Дані звільняються за допомогою видалення, видалення [] або безкоштовно. Виділити повільніше порівняно зі змінними у стеку. Використовується на вимогу для виділення блоку даних для використання програмою. Може мати фрагментацію, коли є багато виділень та звільнень. У C ++ або C дані, створені в купі, будуть вказуватися покажчиками та виділятися відповідно новими або malloc. Може мати помилки розподілу, якщо надсилається запит на занадто великий буфер. Ви можете використовувати купу, якщо ви точно не знаєте, скільки даних вам знадобиться під час роботи або якщо вам потрібно виділити багато даних. Відповідає за витоки пам'яті. Приклад: int foo () { char * pBuffer; // <- ще нічого не виділено (за винятком самого вказівника, який виділений тут у стеці). bool b = істина; // Виділяється на стек. якщо (b) { // Створіть 500 стейтів у стеку буфер символів [500]; // Створення 500 байт на купі pBuffer = новий символ [500]; } // <- тут звільнено буфер, pBuffer - ні } // <--- ой, там витік пам'яті, я мав би викликати delete [] pBuffer; | Найважливішим моментом є те, що купа і стек є загальними термінами для виділення пам'яті. Вони можуть бути реалізовані різними способами, і терміни стосуються основних понять. У стосі предметів предмети розташовуються один на одному в тому порядку, в якому вони були там розміщені, і ви можете видалити лише верхній(не перекидаючи всю справу). Простота стеку полягає в тому, що вам не потрібно вести таблицю, що містить запис кожного розділу виділеної пам'яті; єдиною інформацією про стан, яка вам потрібна, є єдиний вказівник на кінець стека. Щоб розподілити та де-розподілити, ви просто збільшуєте та зменшуєте цей єдиний покажчик. Примітка. Іноді стек може бути реалізований для початку вгорі розділу пам'яті та розширення вниз, а не зростання вгору. У купі немає конкретного порядку розміщення предметів. Ви можете заходити та видаляти предмети в будь-якому порядку, оскільки немає чіткого елемента "зверху". Розподіл купи вимагає ведення повного запису про те, яка пам’ять виділяється, а яка ні, а також певне накладне обслуговування, щоб зменшити фрагментацію, знайти суміжні сегменти пам'яті, достатньо великі, щоб відповідати запитуваному розміру тощо. Пам'ять можна звільнити в будь-який час, залишаючи вільний простір. Іноді розподільник пам'яті буде виконувати такі завдання технічного обслуговування, як дефрагментація пам'яті шляхом переміщення виділеної пам'яті або збір сміття - ідентифікація під час виконання, коли пам'ять більше не входить в область дії, та її вивільнення. Ці зображення повинні досить добре описати два способи розподілу та звільнення пам'яті у стеку та купі. Нім! Наскільки вони контролюються ОС або мовою виконання? Як уже згадувалося, купа і стек є загальними умовами і можуть бути реалізовані різними способами. Комп’ютерні програми зазвичай мають стек, який називається стеком викликів, який зберігає інформацію, що стосується поточної функції, наприклад, вказівник на те, з якої функції вона була викликана, та будь-які локальні змінні. Оскільки функції викликають інші функції, а потім повертаються, стек зростає і зменшується, щоб утримувати інформацію від функцій, що знаходяться нижче в стеці викликів. Програма насправді не має контролю над роботою; це визначається мовою програмування, ОС і навіть архітектурою системи. Куча - це загальний термін, який використовується для будь-якої пам'яті, яка розподіляється динамічно і випадково; тобто не в порядку. Пам'ять, як правило, виділяється ОС, а програма, яка викликає функції API, робить це розподіл. Для управління динамічно розподіленою пам’яттю, яка зазвичай обробляється кодом середовища виконання мови програмування чи середовища, що вимагається, потрібен достатній накладних витрат. Який їх обсяг? Стек викликів є настільки низьким рівнем концепції, що він не відноситься до "сфери дії" у сенсі програмування. Якщо ви розберете якийсь код, ви побачите відносні посилання на стиль стеку вказівника на частини стека, але, що стосується мови вищого рівня, мова встановлює свої власні правила обсягу. Однак одним із важливих аспектів стека є те, що як тільки функція повертається, все локальне для цієї функції негайно звільняється зі стека. Це працює так, як ви очікували б, з огляду на те, як працюють ваші мови програмування. У купі це теж важко визначити. Сфера дії - це те, що піддається ОС, але ваша мова програмування, ймовірно, додає свої правила щодо того, що таке "область дії" у вашому додатку. Архітектура процесора та ОС використовують віртуальну адресацію, яку процесор перетворює на фізичні адреси, є помилки сторінок тощо. Вони відстежують, які сторінки належать яким додаткам. Вам ніколи не потрібно турбуватися з цього приводу, оскільки ви просто використовуєте будь-який метод, який використовує ваша мова програмування, для виділення та звільнення пам'яті та перевірки на наявність помилок (якщо розподіл / звільнення не вдається з будь-якої причини). Що визначає розмір кожного з них? Знову ж таки, це залежить від мови, компілятора, операційної системи та архітектури. Зазвичай стек виділяється заздалегідь, оскільки за визначенням це має бути суцільна пам’ять. Мовний компілятор або ОС визначають його розмір. Ви не зберігаєте величезні шматки даних у стеці, тому вони будуть достатньо великими, щоб їх ніколи не можна було використовувати повністю, за винятком випадків небажаної нескінченної рекурсії (отже, "переповнення стека") або інших незвичних програмних рішень. Куча - це загальний термін для всього, що може бути динамічно розподілено. Залежно від того, з якого погляду ви на це дивитесь, він постійно змінює розмір. У сучасних процесорах та операційних системах точний спосіб його роботи в будь-якому випадку дуже абстрагований, тому вам зазвичай не потрібно сильно турбуватися про те, як це працює глибоко внизу, за винятком того, що (на мовах, де це дозволяє) ви не повинні використовувати пам'ять ви ще не виділили або пам'ять, яку ви звільнили. Що робить його швидшим? Стек швидший, оскільки вся вільна пам’ять завжди суміжна. Не потрібно вести жодного списку всіх сегментів вільної пам’яті, лише один вказівник на поточну вершину стека. Для цього компілятори зазвичай зберігають цей вказівник у спеціальному швидкому реєстрі. Більше того, подальші операції над стеком зазвичай концентруються в дуже сусідніх областях пам'яті, що на дуже низькому рівні добре для оптимізації вбудованим процесоромкеші. | (Я переніс цю відповідь з іншого питання, яке було більш-менш оману цього.) Відповідь на ваше запитання залежить від реалізації та може відрізнятися залежно від компіляторів та архітектур процесорів. Однак тут є спрощене пояснення. І стек, і купа є областями пам'яті, виділеними з базової операційної системи (часто віртуальної пам'яті, яка на вимогу відображається у фізичну пам'ять). У багатопотоковому середовищі кожен потік матиме свій повністю незалежний стек, але вони будуть спільно використовувати купу. Одночасний доступ повинен контролюватися в купі, а в стеку це неможливо. Купи Купа містить зв’язаний список використаних та вільних блоків. Нові розподіли в купі (new або malloc) задовольняються створенням відповідного блоку з одного з вільних блоків. Це вимагає оновлення списку блоків у купі. Ця метаінформація про блоки в купі також зберігається в купі часто на невеликій ділянці безпосередньо перед кожним блоком. У міру зростання купи нові блоки часто виділяються з нижчих адрес у вищі адреси. Таким чином, ви можете думати про купу як про купу блоків пам'яті, яка збільшується в міру виділення пам'яті. Якщо купа занадто мала для розподілу, розмір часто можна збільшити, придбавши більше пам'яті у базової операційної системи. Виділення та вивільнення багатьох дрібних блоків може залишити купу в стані, коли між використаними блоками є багато маленьких вільних блоків. Запит на виділення великого блоку може не вдатися, оскільки жоден із вільних блоків не є достатньо великим, щоб задовольнити запит на розподіл, навіть незважаючи на те, що комбінований розмір вільних блоків може бути досить великим. Це називається фрагментацією купи. Коли вивільнений використаний блок, який знаходиться поруч із вільним блоком, новий вільний блок може бути об’єднаний із сусіднім вільним блоком, щоб створити більший вільний блок, ефективно зменшуючи фрагментацію купи. Стек Стек часто працює в тісному тандемі зі спеціальним регістром на центральному процесорі, який називається покажчиком стека. Спочатку вказівник стека вказує на верх стека (найвища адреса в стеку). Процесор має спеціальні вказівки щодо переміщення значень у стек і викидання їх назад із стеку. Кожне натискання зберігає значення в поточному місці вказівника стека і зменшує вказівник стека. Поп отримує значення, на яке вказує вказівник стека, а потім збільшує вказівник стека (нехай вас не бентежить той факт, що додавання значення до стека зменшує покажчик стека, а видалення значення збільшує його. Пам'ятайте, що стек зростає до знизу). Збережені та отримані значення є значеннями регістрів ЦП. Коли функція називається, ЦП використовує спеціальні інструкції, які штовхають поточний вказівник інструкції, тобто адресу коду, що виконується в стеку. Потім центральний процесор переходить до функції, встановлюючи вказівник інструкції на адресу викликаної функції. Пізніше, коли функція повертається, старий вказівник інструкцій вискакується зі стеку і виконання відновлюється в коді відразу після виклику функції. Коли вводиться функція, покажчик стека зменшується, щоб виділити більше місця в стеці для локальних (автоматичних) змінних. Якщо функція має одну локальну 32-бітову змінну, у стек відкладаються чотири байти. Коли функція повертається, покажчик стека переміщується назад, щоб звільнити виділену область. Якщо функція має параметри, вони надсилаються в стек перед викликом функції. Потім код у функції може переміщатися по стеку з поточного вказівника стека, щоб знайти ці значення. Виклики вкладених функцій працюють як шарм. Кожен новий виклик буде розподіляти параметри функції, адресу повернення та простір для локальних змінних, і ці записи активації можуть бути складені для вкладених викликів і будуть розкручуватися правильно, коли функції повернуться. Оскільки стек є обмеженим блоком пам'яті, ви можете викликати переповнення стека, викликаючи занадто багато вкладених функцій та / або виділяючи занадто багато місця для локальних змінних. Часто область пам'яті, яка використовується для стека, налаштовується таким чином, що запис нижче нижньої (найнижчої адреси) стека спричинить затримку або виняток у ЦП. Потім цей винятковий стан може бути схоплений робочим середовищем і перетворений у якийсь виняток переповнення стека. Чи можна виділити функцію в купі замість стека? Ні, записи активації функцій (тобто локальних або автоматичних змінних) виділяються у стеку, який використовується не тільки для зберігання цих змінних, але й для відстеження викликів вкладених функцій. Те, як управляється купою, насправді залежить від середовища виконання. C використовує malloc, а C ++ використовує нові, але багато інших мов мають збір сміття. Однак стек - це функція більш низького рівня, тісно пов'язана з архітектурою процесора. З тих пір вирощувати купу, коли не вистачає місця, не надто складноце може бути реалізовано у виклику бібліотеки, який обробляє купу. Однак вирощування стека часто неможливо, оскільки переповнення стека виявляється лише тоді, коли занадто пізно; а вимкнення потоку виконання - єдиний життєздатний варіант. | У наступному коді C # public void Method1 () { int i = 4; int y = 2; class1 cls1 = new class1 (); } Ось як управляється пам’яттю Локальні змінні, які повинні тривати лише до тих пір, поки виклик функції надходить у стек. Ця купа використовується для змінних, життя яких ми насправді не знаємо заздалегідь, але ми очікуємо, що вони триватимуть деякий час. У більшості мов критично важливо, щоб під час компіляції ми знали, наскільки велика змінна, якщо ми хочемо зберегти її у стеку. Об'єкти (які змінюються за розміром, коли ми їх оновлюємо) потрапляють у купу, тому що ми не знаємо під час створення, як довго вони триватимуть. У багатьох мовах купа - це сміття, зібране для пошуку об’єктів (наприклад, об’єкта cls1), на які більше немає посилань. У Java більшість об'єктів надходять безпосередньо в купу. У таких мовах, як C / C ++, структури та класи часто можуть залишатися в стеку, коли ви не маєте справу з покажчиками. Більше інформації можна знайти тут: Різниця між виділенням пам’яті стеку та кучі «timmurphy.org і тут: Створення об'єктів у стеку та купі Ця стаття є джерелом малюнка вище: Шість важливих концепцій .NET: стек, купа, типи значень, типи посилань, бокс та розпакування - CodeProject але пам’ятайте, що він може містити деякі неточності. | Стек Коли ви викликаєте функцію, аргументи цієї функції плюс деякі інші накладні витрати поміщаються в стек. Деяка інформація (наприклад, куди повертатися) також там зберігається. Коли ви оголошуєте змінну всередині вашої функції, ця змінна також виділяється в стеку. Виділити стек досить просто, оскільки ви завжди здійснюєте делокацію в зворотному порядку, в якому розподіляєте. Матеріали стеку додаються під час введення функцій, відповідні дані видаляються при виході з них. Це означає, що ви, як правило, залишаєтеся в невеликій області стека, якщо не викликаєте безліч функцій, які викликають безліч інших функцій (або не створюєте рекурсивне рішення). Купи Купи - це загальна назва для того, куди ви зберігаєте дані, які ви створюєте на льоту. Якщо ви не знаєте, скільки космічних кораблів збирається створити ваша програма, ви, швидше за все, будете використовувати новий (або malloc або еквівалентний) оператор для створення кожного космічного корабля. Цей розподіл триватиме деякий час, тому, швидше за все, ми звільнимо речі в іншому порядку, ніж ми їх створили. Таким чином, купа є набагато складнішою, тому що в кінцевому підсумку виникають регіони пам'яті, які невикористовуються, перемежовані фрагментами - пам'ять стає фрагментованою. Пошук вільної пам’яті потрібного вам розміру є складною проблемою. Ось чому купи слід уникати (хоча вона все ще часто використовується). Впровадження Реалізація стека і купи зазвичай залежить від середовища виконання / ОС. Часто ігри та інші додатки, які мають критичну продуктивність, створюють власні рішення для пам'яті, які захоплюють велику частину пам'яті з купи, а потім викладають її всередину, щоб не покладатися на операційну систему для пам'яті. Це практично, лише якщо використання вашої пам’яті сильно відрізняється від норми - тобто для ігор, де ви завантажуєте рівень за одну величезну операцію і можете відбити цілу партію в іншій величезній операції. Фізичне розташування в пам'яті Це менш актуально, ніж ви думаєте, завдяки технології, яка називається віртуальна пам’ять, яка змушує вашу програму думати, що у вас є доступ до певної адреси, де фізичні дані знаходяться десь в іншому місці (навіть на жорсткому диску!) Адреси, які ви отримуєте для стека, зростають, оскільки ваше дерево дзвінків стає глибшим. Адреси купи непередбачувані (тобто конкретна імплементація) і, відверто, не важливі. | Для уточнення, у цій відповіді є неправильна інформація (Томас виправив свою відповідь після коментарів, класно :)). Інші відповіді просто уникають пояснень, що означає статичне розподіл. Тож я пояснитимуть три основні форми розподілу та їх взаємозв’язок із купою, стеком та сегментом даних нижче. Я також покажу кілька прикладів як на C / C ++, так і на Python, щоб допомогти людям зрозуміти. "Статичні" (AKA статично виділені) змінні не виділяються в стеку. Не припускайте цього - багато людей роблять це лише тому, що "статичний" дуже нагадує "стек". Вони насправді не існують ні в стеку, ні в купі. Вони є частиною того, що називається сегментом даних. Однак, як правило, краще враховувати "сферу дії" та "термін служби", а не "стек" та "купу". Область дії стосується того, які частини коду можуть отримати доступ до змінної. Як правило, ми думаємо про локальний обсяг (доступний лише за допомогою поточної функції) проти глобального обсягу (доступний будь-де), хоча обсяг може стати набагато складнішим. Термін служби відноситься до випадків, коли змінна виділяється та вивільняється під час виконання програми. Зазвичай ми думаємо про статичне розподіл (зміннузберігатиметься протягом усієї тривалості програми, що робить її корисною для зберігання однієї і тієї ж інформації в декількох викликах функцій) проти автоматичного розподілу (змінна зберігається лише під час одного виклику функції, що робить її корисною для зберігання інформації, яка використовується лише функція і може бути відкинута після закінчення) проти динамічного розподілу (змінні, тривалість яких визначається під час виконання, замість часу компіляції, наприклад статичного чи автоматичного). Незважаючи на те, що більшість компіляторів та інтерпретаторів реалізують цю поведінку подібним чином з точки зору використання стеків, куп тощо, компілятор може іноді порушувати ці правила, якщо хоче, поки поведінка правильна. Наприклад, завдяки оптимізації локальна змінна може існувати лише в реєстрі або бути повністю видалена, навіть незважаючи на те, що більшість локальних змінних існують у стеку. Як було зазначено в декількох коментарях, ви можете запровадити компілятор, який навіть не використовує стек або купу, а натомість деякі інші механізми зберігання (це робиться рідко, оскільки стеки та купи для цього чудово підходять). Я продемонструю кілька простих анотованих кодів, щоб проілюструвати все це. Найкращий спосіб вчитися - це запускати програму під налагоджувачем і спостерігати за поведінкою. Якщо ви віддаєте перевагу читати python, перейдіть до кінця відповіді :) // Статично виділений у сегменті даних при першому завантаженні програми / DLL // Виділяється при виході програми / DLL // область дії - доступ до неї з будь-якої точки коду int someGlobalVariable; // Статично виділений у сегменті даних при першому завантаженні програми // Виділяється при виході програми / DLL // область дії - доступ до нього з будь-якого місця в цьому конкретному файлі коду static int someStaticVariable; // "someArgument" виділяється у стеку кожного разу, коли викликається MyFunction // "someArgument" звільняється, коли MyFunction повертається // область дії - доступ до неї можливий лише в MyFunction () void MyFunction (int someArgument) { // Статично виділений у сегменті даних при першому завантаженні програми // Виділяється при виході програми / DLL // область дії - доступ до неї можливий лише в MyFunction () static int someLocalStaticVariable; // Виділяється в стеці кожного разу, коли викликається MyFunction // Виділено, коли MyFunction повертається // область дії - доступ до неї можливий лише в MyFunction () int someLocalVariable; // Вказівник * виділяється в стеку кожного разу, коли викликається MyFunction // Цей покажчик звільняється, коли MyFunction повертається // область дії - вказівник доступний лише в MyFunction () int * someDynamicVariable; // Цей рядок призводить до виділення цілого числа у купі // коли цей рядок виконується. Зауважте, це не на початку // виклик MyFunction (), як автоматичні змінні // сфера дії - лише код у MyFunction () може отримати доступ до цього простору // * через цю конкретну змінну *. // Однак, якщо ви передаєте адресу десь в іншому місці, цей код // також може отримати до нього доступ someDynamicVariable = new int; // Цей рядок звільняє простір для цілого числа в купі. // Якби ми його не написали, пам'ять "просочилася". // Зверніть увагу на принципову різницю між стеком і купою // купою потрібно керувати. Стек управляється нами. видалити someDynamicVariable; // В інших випадках, замість того, щоб звільнити цей простір купи // може зберігати адресу десь більш постійно для подальшого використання. // Деякі мови навіть дбають про звільнення для вас ... але // завжди про це потрібно дбати під час виконання за допомогою якогось механізму. // Коли функція повертається, someArgument, someLocalVariable // та вказівник someDynamicVariable звільняються. // Простір, на який вказує someDynamicVariable, уже був // звільнено до повернення. повернення; } // Зверніть увагу, що someGlobalVariable, someStaticVariable та // someLocalStaticVariable продовжує існувати і не є // звільняється до виходу програми. Особливо гострим прикладом того, чому важливо розрізняти час життя та область дії, є те, що змінна може мати локальну область дії, але статичну тривалість життя - наприклад, "someLocalStaticVariable" у зразку коду вище. Такі змінні можуть зробити наші загальні, але неформальні звички іменування дуже заплутаними. Наприклад, коли ми говоримо "локальний", ми, як правило, маємо на увазі "локально визначений автоматично розміщений змінний", а коли ми говоримо глобальний, ми зазвичай маємо на увазі "глобально визначений статично розміщений змінний". На жаль, коли справа стосується таких речей, як "статично розміщені змінні, що мають масштаб файлу", багато людей просто кажуть ... "так ???". Деякі варіанти синтаксису в C / C ++ посилюють цю проблему - наприклад, багато людей вважають, що глобальні змінні не "статичні" через синтаксис, показаний нижче. int var1; // Має глобальний обсяг і статичне розподіл статичний int var2; // Має обсяг файлу та статичне розміщення int main () {return 0;} Зауважте, що введення ключового слова "static" у декларацію вище заважає var2 мати глобальний обсяг. Тим не менше, глобальний var1 має статичне розподіл. Це неінтуїтивно! З цієї причини я намагаюся ніколи не використовувати слово "статичний", описуючи область дії, а натомість кажу щось на зразок "файл" або "обмежений файл". Однак багато людей використовують фразу "static" або "static scope" для опису змінної, до якої можна отримати доступ лише з одного кодового файлу. У контексті терміну дії, "static" завжди означає, що змінна виділяється під час запуску програми та виводиться з місця при виході програми. Деякі люди вважають ці поняття специфічними для C / C ++. Вони не. Наприклад, наведений нижче зразок Python ілюструє всі три типи розподілу (існують деякі тонкі відмінності в інтерпретованих мовах, про які я тут не буду). від datetime імпорт datetime клас Тварина: _FavoriteFood = 'Невизначено' # _FavoriteFood виділено статично def PetAnimal (самостійно): curTime = datetime.time (datetime.now ()) # curTime призначається автоматично print ("Дякую, що погладили. Але це" + str (curTime) + ", ви повинні нагодувати мене. Моя улюблена їжа -" + self._FavoriteFood) клас Кішка (Тварина): _FavoriteFood = 'tuna' # Примітка, оскільки ми замінюємо, клас Cat має свою статично виділену змінну _FavoriteFood, відмінну від Animal's клас Собака (тварина): _FavoriteFood = 'steak' # Так само клас Dog отримує власну статичну змінну. Важливо зазначити - ця одна статична змінна є спільною для всіх екземплярів Dog, отже, вона не є динамічною! якщо __name__ == "__основна__": вуса = Cat () # Динамічно виділено fido = Dog () # Динамічно виділено rinTinTin = Dog () # Динамічно виділено вуса.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () Dog._FavoriteFood = 'молочні кістки' вуса.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () # Результат: # Дякую, що погладили мене. Але це 13: 05: 02.255000, ви повинні нагодувати мене. Моя улюблена їжа - тунець # Дякую, що погладили мене. Але це 13: 05: 02.255000, ви повинні нагодувати мене. Моя улюблена їжа - стейк # Дякую, що погладили мене. Але це 13: 05: 02.255000, ти повинен нагодувати мене. Моя улюблена їжа - стейк # Дякую, що погладили мене. Але це 13: 05: 02.255000, ви повинні нагодувати мене. Моя улюблена їжа - тунець # Дякую, що погладили мене. Але це 13: 05: 02.255000, ви повинні нагодувати мене. Моя улюблена їжа - це молочні кістки # Дякую, що погладили мене. Але це 13: 05: 02.256000, ти повинен мене нагодувати. Моя улюблена їжа - це молочні кістки | Інші досить добре відповіли на широкі штрихи, тому я додам кілька деталей. Стек і купа не повинні бути єдиними. Поширена ситуація, коли у вас є більше одного стека, якщо у вас є більше одного потоку в процесі. У цьому випадку кожна нитка має свій власний стек. Ви також можете мати більше однієї купи, наприклад, деякі конфігурації DLL можуть призвести до того, що різні бібліотеки DLL розподіляються з різних куп, тому загалом поганою ідеєю є звільнення пам'яті, виділеної іншою бібліотекою. У C ви можете отримати перевагу розподілу змінної довжини за допомогою використання alloca, який розподіляє в стеку, на відміну від alloc, який розподіляє в купі. Ця пам’ять не збереже ваш оператор повернення, але корисний для буфера скретчів. Створення величезного тимчасового буфера для Windows, яким ви мало використовуєте, не є безкоштовним. Це пояснюється тим, що компілятор генерує цикл перевірки стека, який викликається кожного разу, коли вводиться ваша функція, щоб переконатися, що стек існує (оскільки Windows використовує одну сторінку захисту в кінці стека, щоб визначити, коли йому потрібно виростити стек. Якщо ви отримаєте доступ до пам'яті більше, ніж одна сторінка з кінця стека, ви зазнаєте збою). Приклад: анулювати мою функцію () { char великий [10000000]; // Виконайте те, що використовує лише перші 1 тисячі великих 99% часу. } | Інші безпосередньо відповіли на ваше запитання, але, намагаючись зрозуміти стек і купу, я вважаю корисним врахувати розміщення пам'яті традиційного процесу UNIX (без потоків та розподілювачів на основі mmap ()). Веб-сторінка глосарію управління пам’яттю містить схему цього розташування пам’яті. Стек і купа традиційно розташовуються на протилежних кінцях віртуального адресного простору процесу. Стек автоматично зростає при доступі до розміру, встановленого ядром (який можна регулювати за допомогою setrlimit (RLIMIT_STACK, ...)). Купа зростає, коли розподільник пам'яті викликає системний виклик brk () або sbrk (), відображаючи більше сторінок фізичної пам'яті у віртуальний адресний простір процесу. У системах без віртуальної пам'яті, таких як деякі вбудовані системи, часто застосовується однаковий базовий макет, за винятком того, що розмір стека і купи є фіксованим. Однак в інших вбудованих системах (таких як ті, що базуються на мікроконтролерах Microchip PIC), стек програм є окремим блоком пам'яті, який не може бути адресований інструкціями переміщення даних, і може бути змінений або опосередковано прочитаний за допомогою інструкцій потоку програм (дзвінок повернення тощо). Інші архітектури, такі як процесори Intel Itanium, мають кілька стеків. У цьому сенсі стек є елементом архітектури центрального процесора. | Стек - це порціяпам'яті, якою можна маніпулювати за допомогою декількох ключових інструкцій мовою збірки, таких як 'pop' (видалення та повернення значення зі стека) та 'push' (натискання значення в стек), але також виклик (виклик підпрограми - штовхає адресу для повернення в стек) і return (повернення з підпрограми - це вискакує адресу зі стеку і переходить до неї). Це область пам'яті під регістром вказівника стека, яку можна встановити за потреби. Стек також використовується для передачі аргументів у підпрограми, а також для збереження значень у регістрах перед викликом підпрограм. Купа - це частина пам'яті, яка надається додатку операційною системою, як правило, за допомогою системного виклику, такого як malloc. У сучасних ОС ця пам’ять - це набір сторінок, доступ до яких має лише процес виклику. Розмір стека визначається під час виконання і, як правило, не зростає після запуску програми. У програмі на С стек повинен бути достатньо великим, щоб містити кожну змінну, оголошену в межах кожної функції. Купа буде рости динамічно, якщо це потрібно, але ОС в кінцевому підсумку здійснює виклик (вона часто збільшить купу більше, ніж значення, яке вимагає malloc, так що принаймні деяким майбутнім mallocs не потрібно буде повертатися до ядра, щоб отримати більше пам'яті. Ця поведінка часто налаштовується) Оскільки ви виділили стек перед запуском програми, вам ніколи не потрібно робити malloc, перш ніж ви зможете використовувати стек, тому це невелика перевага. На практиці дуже важко передбачити, що буде швидко, а що повільно в сучасних операційних системах, які мають підсистеми віртуальної пам'яті, оскільки те, як реалізовані сторінки та де вони зберігаються, є деталлю реалізації. | Що таке стек? Стек - це купа предметів, як правило, акуратно розташованих. Стеки в обчислювальних архітектурах - це області пам’яті, куди дані додаються або видаляються методом «останній у першому». У багатопотоковій програмі кожен потік матиме свій стек. Що таке купа? Купи - це неохайна колекція речей, складених безладно. У обчислювальних архітектурах купа - це область динамічно виділеної пам'яті, яка автоматично управляється операційною системою або бібліотекою менеджера пам'яті. Пам'ять у купі регулярно виділяється, вивільняється та змінюється розмір під час виконання програми, і це може призвести до проблеми, яка називається фрагментацією. Фрагментація відбувається, коли об’єкти пам’яті виділяються між собою невеликими пробілами, які є замалими для розміщення додаткових об’єктів пам’яті. Чистий результат - це відсоток простору купи, який не використовується для подальшого розподілу пам'яті. Обидва разом У багатопотоковій програмі кожен потік матиме свій стек. Але всі різні нитки поділять купу. Оскільки різні потоки спільно використовують купу в багатопотоковій програмі, це також означає, що між потоками повинна бути певна координація, щоб вони не намагалися отримати доступ до одних і тих самих фрагментів пам'яті в купі в в той же час. Що швидше - стек чи купа? І чому? Стек набагато швидший за купу. Це пов’язано з тим, як виділяється пам’ять у стеку. Розподілити пам'ять у стеку так само просто, як перемістити вказівник стека вгору. Для початківців програмістів, мабуть, є гарною ідеєю використовувати стек, оскільки це простіше. Оскільки стек невеликий, ви хотіли б використовувати його, коли точно знаєте, скільки пам'яті вам знадобиться для ваших даних, або якщо ви знаєте, що розмір ваших даних дуже малий. Краще використовувати купу, коли ви знаєте, що вам знадобиться багато пам'яті для ваших даних, або ви просто не впевнені, скільки пам’яті вам знадобиться (наприклад, з динамічним масивом). Модель пам'яті Java Стек - це область пам'яті, де зберігаються локальні змінні (включаючи параметри методу). Що стосується змінних об’єктів, це просто посилання (покажчики) на фактичні об’єкти в купі. Кожного разу, коли об'єкт створюється за допомогою екземпляра, шматок купи пам'яті відводиться для зберігання даних (стану) цього об'єкта. Оскільки об'єкти можуть містити інші об'єкти, деякі з цих даних насправді можуть містити посилання на ці вкладені об'єкти. | Думаю, багато інших людей давали вам переважно правильні відповіді з цього приводу. Однак одна деталь, яку пропустили, полягає в тому, що "купу" насправді слід називати "безкоштовним магазином". Причиною такого розрізнення є те, що оригінальний безкоштовний магазин був реалізований із структурою даних, відомою як "біноміальна купа". З цієї причини виділення з ранніх реалізацій malloc () / free () було виділенням з купи. Однак у наш сучасний час більшість безкоштовних сховищ реалізуються з дуже досконалими структурами даних, які не є біноміальними кучами. | Ви можете зробити кілька цікавих речей зі стеком. Наприклад, у вас є такі функції, як alloca (припускаючи, що ви можете пройти рясні попередження щодо його використання), яка є формою malloc, якаспеціально використовує стек, а не купу, для пам'яті. Тим не менше, помилки пам’яті на основі стеку - це одне з найгірших, які я відчував. Якщо ви використовуєте кучу пам'яті і перевищуєте межі виділеного блоку, у вас є гідний шанс викликати помилку сегмента. (Не на 100%: ваш блок може бути випадково суміжним з іншим, який ви раніше виділили.) Але оскільки змінні, створені в стеці, завжди суміжні між собою, виписування за межі може змінити значення іншої змінної. Я довідався, що коли я відчуваю, що моя програма перестала підкорятися законам логіки, це, мабуть, переповнення буфера. | Просто, стек - це місце, де створюються локальні змінні. Крім того, кожного разу, коли ви викликаєте підпрограму, лічильник програми (вказівник на наступну машинну інструкцію) та будь-які важливі регістри, а іноді параметри штовхаються у стек. Потім будь-які локальні змінні всередині підпрограми виштовхуються в стек (і використовуються звідти). Коли підпрограма закінчується, всі речі вискакують із стеку. Дані ПК та реєстру отримуються та повертаються туди, де вони були, як вони з’являються, щоб ваша програма могла піти своїм веселим шляхом. Куча - це область пам’яті, з якої здійснюється динамічне виділення пам’яті (явні виклики «новий» або «виділити»). Це спеціальна структура даних, яка може відстежувати блоки пам’яті різного розміру та стан їх розподілу. У "класичних" системах оперативна пам'ять була викладена таким чином, що вказівник стека починався внизу пам'яті, вказівник купи починався вгорі, і вони зростали один до одного. Якщо вони збігаються, у вас закінчилася оперативна пам’ять. Однак це не працює з сучасними багатопотоковими ОС. Кожен потік повинен мати свій власний стек, і вони можуть створюватися динамічно. | З WikiAnwser. Стек Коли функція або метод викликає іншу функцію, яка, у свою чергу, викликає іншу функцію тощо, виконання всіх цих функцій залишається призупиненим, поки остання функція не поверне своє значення. Цей ланцюжок призупинених викликів функцій є стеком, оскільки елементи в стеку (виклики функцій) залежать один від одного. Стек важливо враховувати при обробці винятків та виконанні потоків. Купи Купа - це просто пам'ять, яка використовується програмами для зберігання змінних. Елементи купи (змінні) не мають залежностей між собою і завжди можуть отримати довільний доступ у будь-який час. | Стек Дуже швидкий доступ Не потрібно явно де-розподіляти змінні Простір ефективно управляється процесором, пам’ять не стане фрагментованою Тільки локальні змінні Обмеження розміру стека (залежно від ОС) Змінні не можна змінити Купи До змінних можна отримати доступ у всьому світі Без обмеження обсягу пам'яті (Відносно) повільніший доступ Не гарантується ефективне використання простору, пам'ять може стати фрагментованою з часом, коли блоки пам'яті виділяються, а потім звільняються Ви повинні керувати пам'яттю (ви відповідаєте за розподіл і звільнення змінних) Змінні можна змінювати за допомогою realloc () | Коротко Стек використовується для статичного розподілу пам'яті і купи для динамічного розподілу пам'яті, обидва вони зберігаються в оперативній пам'яті комп'ютера. Детально Стек Стек - це структура даних "LIFO" (остання вхід, перша вихід), що управляється та оптимізується ЦП досить тісно. Кожного разу, коли функція оголошує нову змінну, вона "виштовхується" у стек. Потім кожного разу, коли функція виходить, усі змінні, що надсилаються цією функцією в стек, звільняються (тобто вони видаляються). Після звільнення змінної стека ця область пам'яті стає доступною для інших змінних стека. Перевага використання стека для зберігання змінних полягає в тому, що пам’ять управляється вами. Вам не потрібно виділяти пам’ять вручну або звільняти її, коли вона вам більше не потрібна. Більше того, оскільки центральний процесор організовує пам’ять стека настільки ефективно, читання та запис змінних у стек відбувається дуже швидко. Більше можна знайти тут. Купи Куча - це область пам’яті вашого комп’ютера, яка не управляється автоматично для вас і не настільки жорстко управляється процесором. Це більш вільно плаваюча область пам'яті (і більша). Щоб розподілити пам'ять у купі, ви повинні використовувати malloc () або calloc (), які є вбудованими функціями C. Виділивши пам'ять у купі, ви несете відповідальність за використання free () для вивільнення цієї пам'яті, коли вона вам більше не потрібна. Якщо ви цього не зробите, ваша програма матиме те, що називається витоком пам'яті. Тобто пам’ять у купі все одно буде виділено (і не буде доступною для інших процесів). Як ми побачимо у розділі налагодження, існує інструмент під назвою Valgrind, який може допомогти вам виявити витоки пам'яті. На відміну від стека, купа не має обмежень щодо розміру змінного розміру (крім очевидних фізичних обмежень вашого комп'ютера). Купчаста пам’ять трохи повільніше читається і записується, оскільки для доступу до пам’яті в купі потрібно використовувати покажчики. Про вказівники ми поговоримо незабаром. На відміну від стека,змінні, створені в купі, доступні будь-якій функції будь-де у вашій програмі. Змінні купи по суті є глобальними за обсягом. Більше можна знайти тут. Змінні, виділені в стеку, зберігаються безпосередньо в пам'яті, і доступ до цієї пам'яті дуже швидкий, і її розподіл вирішується, коли програма компілюється. Коли функція або метод викликає іншу функцію, яка, у свою чергу, викликає іншу функцію тощо, виконання всіх цих функцій залишається призупиненим, поки остання функція не поверне своє значення. Стек завжди зарезервований у порядку LIFO, останній зарезервований блок завжди є наступним звільненим блоком. Це робить дуже простим відстеження стека, звільнення блоку з стеку є не що інше, як регулювання одного покажчика. Змінні, виділені в купі, виділяють пам'ять під час виконання, і доступ до цієї пам'яті трохи повільніший, але розмір купи обмежується лише розміром віртуальної пам'яті. Елементи купи не мають залежностей між собою і завжди можуть отримати довільний доступ у будь-який час. Ви можете виділити блок у будь-який час і звільнити його в будь-який час. Це робить набагато складнішим відстеження того, які частини купи виділені чи вільні в будь-який момент часу. Ви можете використовувати стек, якщо точно знаєте, скільки даних потрібно виділити до часу компіляції, і він не надто великий. Ви можете використовувати купу, якщо ви точно не знаєте, скільки даних вам знадобиться під час виконання або якщо вам потрібно виділити багато даних. У багатопотоковій ситуації кожен потік матиме свій повністю незалежний стек, але вони будуть спільно використовувати купу. Стек залежить від потоку, а купа - для програми. Стек важливо враховувати при обробці винятків та виконанні потоків. Кожен потік отримує стек, тоді як для програми зазвичай є лише одна купа (хоча нерідкі випадки, коли є кілька куп для різних типів розподілу). Якщо під час виконання додатку потрібно більше купи, він може виділити пам'ять із вільної пам'яті, а якщо стеку потрібна пам'ять, він може виділити пам'ять із вільної пам'яті, виділеної для програми. Навіть докладніше тут і тут. Тепер перейдіть до відповідей на ваше запитання. Наскільки вони контролюються ОС або мовою виконання? ОС створює стек для кожного потоку рівня системи під час створення потоку. Зазвичай ОС викликається мовою виконання, щоб виділити купу для програми. Більше можна знайти тут. Який їх обсяг? Вже дано зверху. "Ви можете використовувати стек, якщо точно знаєте, скільки даних вам потрібно виділити до часу компіляції, і він не надто великий. Ви можете використовувати купу, якщо не знаєте точно, скільки даних вам знадобиться під час виконання або потрібно виділити багато даних ". Більше можна знайти тут. Що визначає розмір кожного з них? Розмір стека встановлюється ОС при створенні потоку. Розмір купи встановлюється під час запуску програми, але він може зростати у міру необхідності місця (розподільник вимагає більше оперативної пам'яті в операційній системі). Що робить його швидшим? Розподіл стека відбувається набагато швидше, оскільки все, що насправді робить, - це переміщення вказівника стека. Використовуючи пули пам'яті, ви можете отримати порівнянну продуктивність при розподілі купи, але це має невелику додаткову складність та власні головні болі. Крім того, стек проти купи - це не лише оцінка продуктивності; це також багато розповідає про очікуваний термін служби об’єктів. Деталі можна знайти тут. | Добре, просто і коротко кажучи, вони означають замовлене, а не замовлене ...! Стек: У елементах стека речі потрапляють один на одного, це означає, що їх буде швидше та ефективніше обробляти! ... Отже, завжди є індекс, який вказує на конкретний елемент, також обробка буде швидшою, існує взаємозв’язок між елементами! Купи: жодного замовлення, обробка буде повільнішою, а значення переплутані разом із певним порядком чи індексом ... є випадкові випадки, і між ними немає взаємозв’язку ... тому час виконання та використання може бути різним ... Я також створюю зображення нижче, щоб показати, як вони можуть виглядати: | стек, купа та дані кожного процесу у віртуальній пам'яті: | У 1980-х UNIX поширювався, як зайчики, з великими компаніями, які котували свої власні. Exxon мав таку, як і десятки брендів, втрачених в історії. Розклад пам'яті визначався на розсуд багатьох розробників. Типова програма на С була викладена на пам'ять з можливість збільшити, змінивши значення brk (). Як правило, HEAP був трохи нижче цього значення brk і збільшення brk збільшило кількість доступної купи. Сингл STACK зазвичай був областю нижче HEAP, що було трактом пам'яті не містить нічого цінного до початку наступного фіксованого блоку пам'яті. Наступним блоком часто був CODE, який можна було замінити даними стека в одному з відомих хаків своєї епохи. Типовим блоком пам'яті був BSS (нульовий блокзначення) який випадково не був обнулений у пропозиції одного виробника. Іншим був DATA, що містить ініціалізовані значення, включаючи рядки та числа. Третім був КОД, що містить ЕПТ (час виконання C), основні, функції та бібліотеки. Поява віртуальної пам'яті в UNIX змінює багато обмежень. Не існує об’єктивної причини, чому ці блоки повинні бути суміжними, або закріплений за розміром, або замовлений певним чином зараз. Звичайно, раніше UNIX був Multics, який не страждав від цих обмежень. Ось схема, що показує один із макетів пам'яті тієї епохи. | Пара центів: Думаю, непогано буде намалювати пам'ять графічно і простіше: Стрілки - показують, де росте стек і купа, розмір стека процесів має обмеження, визначені в ОС, обмеження розміру стека потоків за параметрами в API створення потоку зазвичай. Купи зазвичай обмежує процесом максимальний обсяг віртуальної пам'яті, наприклад, для 32-бітових 2-4 ГБ, наприклад. Так простий спосіб: купа процесів є загальною для процесу та всіх потоків усередині, використовуючи для розподілу пам'яті в загальному випадку з чимось на зразок malloc (). Стек - це швидка пам'ять для зберігання в загальних випадках покажчиків повернення функції та змінних, що обробляються як параметри у виклику функції, локальні змінні функції. | Оскільки деякі відповіді пішли безглуздо, я збираюся внести свій кліщ. Дивно, але ніхто не згадував, що кілька (тобто не пов’язаних із кількістю запущених потоків на рівні ОС) стеки викликів можна знайти не лише в екзотичних мовах (PostScript) або платформах (Intel Itanium), але також у волокнах, зелених потоках та деякі реалізації корутин. Волокна, зелені нитки та супровідні програми багато в чому схожі, що призводить до великої плутанини. Різниця між волокнами та зеленими нитками полягає в тому, що перші використовують кооперативну багатозадачність, тоді як другі можуть мати або кооперативну, або переважну (або навіть обидві). Про різницю між волокнами та корутинами див. Тут. У будь-якому випадку, мета і волокон, і зелених ниток, і супровідних програм полягає в тому, що кілька функцій виконуються одночасно, але не паралельно (див. Це питання SO для розрізнення) в межах одного потоку на рівні ОС, передаючи управління вперед і назад одна від одної організовано. Використовуючи волокна, зелені нитки або програми, зазвичай ви маєте окремий пакет для кожної функції. (Технічно, не просто стек, а цілий контекст виконання відноситься до кожної функції. Найголовніше, що реєструється ЦП.) Для кожного потоку існує стільки стеків, скільки одночасно запущених функцій, і потік перемикається між виконанням кожної функції відповідно до логіки вашої програми. Коли функція запускається до кінця, її стек руйнується. Отже, кількість і тривалість стеків є динамічними і не визначаються кількістю потоків на рівні ОС! Зверніть увагу, що я сказав "зазвичай мають окремий стек для кожної функції". Існують як штабельні, так і нестійкі реалізації курутин. Найбільш помітними стековими реалізаціями С ++ є Boost.Coroutine та async / await від Microsoft PPL. (Однак у відновлюваних функціях C ++ (інакше "асинхронізація та очікування"), які були запропоновані для C ++ 17, швидше за все, будуть використовуватися безпроблемні програми.) Очікується пропозиція волокон до стандартної бібліотеки C ++. Крім того, є деякі сторонні бібліотеки. Зелені нитки надзвичайно популярні в таких мовах, як Python та Ruby. | Мені є чим поділитися, хоча основні моменти вже висвітлені. Стек Дуже швидкий доступ. Зберігається в оперативній пам'яті. Тут завантажуються виклики функцій разом із переданими локальними змінними та параметрами функції. Простір звільняється автоматично, коли програма виходить за межі області дії. Зберігається в послідовній пам’яті. Купи Повільний доступ порівняно до Stack. Зберігається в оперативній пам'яті. Тут зберігаються динамічно створені змінні, що згодом вимагає звільнення виділеної пам'яті після використання. Зберігається скрізь, де зроблено виділення пам'яті, доступ до якого здійснюється вказівником завжди. Цікава примітка: Якби виклики функції зберігалися в купі, це призвело б до 2 брудних точок: Завдяки послідовному зберіганню в стеку, виконання є швидшим. Зберігання в купі призвело б до величезного споживання часу, завдяки чому вся програма виконувалася б повільніше. Якби функції зберігалися в купі (брудна пам'ять, на яку вказує вказівник), не було б способу повернутися до адреси абонента назад (який стек дає через послідовне зберігання в пам'яті). | Оце Так! Так багато відповідей, і я не думаю, що хтось із них зрозумів це правильно ... 1) Де і що вони знаходяться (фізично в пам’яті справжнього комп’ютера)? Стек - це пам’ять, яка починається як найвища адреса пам’яті, призначена для зображення вашої програми, а потім вона зменшується у значенні звідти. Він зарезервований для параметрів функції, що викликаються, і для всіх тимчасових змінних, що використовуються у функціях. Є дві купи: державна та приватна. Приватна купа починається на 16-байтній межі (для 64-розрядних програм) або 8-байтовій межі (для 32-розрядних програм) після останнього байта коду у вашій програмі, а потім збільшуєтьсязначення звідти. Його також називають купою за замовчуванням. Якщо приватна купа стає занадто великою, вона перекриватиме область стека, як і стек, якщо вона стає занадто великою. Оскільки стек починається з вищої адреси і рухається вниз до нижчої адреси, при належному злому ви можете зробити стек настільки великим, що він буде перевищувати область приватної купи і перекривати область коду. Тоді фокус полягає в тому, щоб перекрити достатньо області коду, яку ви можете підключити до коду. Зробити це трохи складно, і ви ризикуєте збій програми, але це легко і дуже ефективно. Публічна купа розміщена у власному просторі пам’яті за межами простору зображень вашої програми. Саме ця пам'ять буде вилучена на жорсткий диск, якщо ресурсів пам'яті стане недостатньо. 2) Наскільки вони контролюються ОС або мовою виконання? Стеком керує програміст, приватною купою керує ОС, а публічною купою ніхто не керує, оскільки це послуга ОС - ви робите запити, і вони отримують або відмовляють. 2b) Який їх обсяг? Усі вони є загальними для програми, але їх зміст може бути приватним, загальнодоступним або глобальним. 2в) Що визначає розмір кожного з них? Розмір стека та приватна купа визначаються параметрами середовища виконання вашого компілятора. Загальнодоступна купа ініціалізується під час виконання за допомогою параметра розміру. 2г) Що робить його швидшим? Вони розроблені не так швидко, вони призначені бути корисними. Те, як програміст їх використовує, визначає, чи вони "швидкі" чи "повільні" REF: https://norasandler.com/2019/02/18/Write-a-Compiler-10.html https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate | Багато відповідей є правильними як концепції, але ми повинні зазначити, що стек необхідний апаратному забезпеченню (тобто мікропроцесору), щоб дозволити виклик підпрограм (CALL мовою асемблеру ..). (OOP хлопці називатимуть це методами) У стеку ви зберігаєте адреси повернення, а дзвінок → push / ret → pop керується безпосередньо в апаратному забезпеченні. Ви можете використовувати стек для передачі параметрів .. навіть якщо це повільніше, ніж використання регістрів (скаже гуру мікропроцесора чи хороша книга про BIOS 1980-х років ...) Без стека жоден мікропроцесор не може працювати. (ми не можемо уявити програму, навіть мовою асемблера, без підпрограм / функцій) Без купи це може. (Програма асемблерної мови може працювати без, оскільки купа - це концепція ОС, як malloc, тобто виклик ОС / Lib. Використання стека відбувається швидше, як: Це апаратне забезпечення, і навіть push / pop дуже ефективні. malloc вимагає входу в режим ядра, використання блокування / семафору (або інших примітивів синхронізації), виконання якогось коду, та управління деякими структурами, необхідними для відстеження розподілу. | Купи - це область динамічно розподіленої пам'яті, яка автоматично управляється операційною системою або бібліотекою диспетчера пам'яті. Ви можете виділити блок у будь-який час і звільнити його в будь-який час. Розподіл купи вимагає ведення повного запису про те, яка пам’ять виділяється, а що ні, а також певне накладне обслуговування, щоб зменшити фрагментацію, знайти суміжні сегменти пам'яті, достатньо великі, щоб відповідати запитуваному розміру тощо. Пам'ять можна звільнити в будь-який час, залишаючи вільний простір. У міру зростання купи нові блоки часто виділяються з нижчих адрес у вищі адреси. Таким чином, ви можете думати про купу як про купу блоків пам'яті, яка збільшується в міру виділення пам'яті. Якщо купа занадто мала для розподілу, розмір часто можна збільшити, придбавши більше пам'яті у базової операційної системи. Пам'ять, виділена з купи, буде залишатися виділеною доти, доки не відбудеться одне з наступного: Пам'ять звільняється Програма завершується Стек: Зберігається в оперативній пам’яті комп’ютера, як і купа. Змінні, створені в стеку, вийдуть за межі обсягу та автоматично звільняться. Набагато швидше виділити в порівнянні зі змінними в купі. Зберігає локальні дані, адреси повернення, що використовуються для передачі параметрів. Може мати переповнення стека, коли використовується занадто велика частина стека (в основному від нескінченної або занадто глибокої рекурсії, дуже великих виділень). Ви б використовували стек, якщо точно знаєте, скільки даних вам потрібно виділити до часу компіляції, і він не надто великий. Зазвичай має максимальний розмір, який уже визначений, коли ваша програма починається. Купи: Зберігається в пам’яті комп’ютера, як і стек. У C ++ змінні в купі повинні бути знищені вручну і ніколи випадають із сфери дії. Дані звільняються за допомогою видалення, видалення [] або безкоштовно. Виділити повільніше порівняно зі змінними у стеку. Використовується на вимогу для виділення блоку даних для використання програмою. Може мати фрагментацію, коли є багато виділень і звільнення. У C ++ або C дані, створені в купі, будуть вказуватися покажчиками і виділяється відповідно новим або malloc. Може мати помилки розподілу, якщо запитується занадто великий буфер бути виділеним. вивикористовуватиме купу, якщо ви точно не знаєте, скільки даних знадобиться під час роботи або якщо вам потрібно виділити багато даних. Відповідає за витоки пам'яті. | Стек - це, по суті, просто доступна пам’ять, яка просто керує своїми елементами як стек для колодязя. На стек можуть потрапляти лише предмети, розмір яких відомий заздалегідь. Це стосується чисел, рядків, булевих значень. Купи - це пам’ять для елементів, для яких ви не можете визначити попередньо точні розміри та структура. Оскільки об'єкти та масиви можуть мутувати і змінити під час виконання, вони повинні піти в купу. Джерело: Academind | Стек і купа процесора фізично пов'язані з тим, як процесор і регістри працюють з пам'яттю, як працює мова машинної збірки, а не самі мови високого рівня, навіть якщо ці мови можуть вирішити дрібниці. Усі сучасні центральні процесори працюють з "однаковою" теорією мікропроцесорів: всі вони базуються на так званому "регістрі", а деякі призначені для "стека" для підвищення продуктивності. Усі процесори мають регістри стеків з самого початку, і вони завжди були тут, як я можу говорити, як я знаю. Мови збірки однакові з самого початку, незважаючи на варіації ... аж до Microsoft та її проміжної мови (IL), які змінили парадигму на мову збірки віртуальної машини OO. Тож у майбутньому ми зможемо мати CPU CLI / CIL (один проект MS). Процесори мають регістри стеків для пришвидшення доступу до пам’яті, але вони обмежені порівняно з використанням інших регістрів для отримання повного доступу до всієї доступної пам’яті процесора. Саме тому ми говорили про розподіл стеків і купи. Підводячи підсумок, і загалом, куча є простою і повільною і призначена для "глобальних" екземплярів та вмісту об'єктів, оскільки стек є невеликим і швидким, а також для "локальних" змінних та посилань (приховані вказівники, щоб забути ними керувати). Отже, коли ми використовуємо нове ключове слово в методі, посилання (int) створюється в стеку, але об’єкт і весь його вміст (типи значень, а також об’єкти) створюються в купі, якщо я пам’ятаю. Але локальні елементарні типи значень та масиви створюються у стеку. Різниця в доступі до пам'яті полягає на рівні посилань на клітинки: адресація купи, загальної пам’яті процесу, вимагає більшої складності з точки зору обробки регістрів ЦП, ніж стек, який „більше” локально з точки зору адресації, оскільки стек ЦП регістр використовується як базова адреса, якщо я пам’ятаю. Ось чому, коли у нас дуже довгі або нескінченні повторювані дзвінки або цикли, ми отримуємо переповнення стека швидко, не заморожуючи систему на сучасних комп'ютерах ... C # Heap (ing) Vs Stack (ing) у .NET Стек проти купи: знай різницю Розподіл пам'яті статичного класу, де він зберігається C # Що і де є стек і купа? https://en.wikipedia.org/wiki/Memory_management https://en.wikipedia.org/wiki/Stack_register Мовні ресурси асамблеї: Підручник з програмування асамблеї Посібники розробника програмного забезпечення для архітектур Intel® 64 та IA-32 | Дякую за справді хорошу дискусію, але як справжній нуб цікаво, де зберігаються інструкції? НА ПОЧАТКУ вчені вирішували між двома архітектурами (фон НЕЙМАН, де все вважається ДАНИМ, та ГАРВАРД, де область пам'яті була зарезервована для інструкцій, а інша - для даних). Зрештою, ми пішли з дизайном фон Неймана, і тепер все вважається "однаковим". Це ускладнило мене, коли я вчився складанню https://www.cs.virginia.edu/~evans/cs216/guides/x86.html тому що вони говорять про регістри та покажчики стека. Все вище йдеться про ДАНІ. Я припускаю, що оскільки інструкція - це визначена річ із певним розміром пам'яті, вона буде надходити в стек, і тому всі "ті" регістри, обговорені в збірці, знаходяться в стеку. Звичайно, тоді з’явилося об’єктно-орієнтоване програмування з інструкціями та даними, що об’єднались у структуру, яка була динамічною, тож тепер інструкції також зберігатимуться у купі? | Високоактивне запитання. Заробіть 10 репутації, щоб відповісти на це питання. Вимога про репутацію допомагає захистити це питання від спаму та відсутності відповідей. Не відповідь, яку ви шукаєте? Перегляньте інші запитання, позначені тегом стека управління пам’яттю, мовний агностик, динамічне виділення пам’яті, або задайте власне запитання.